package com.kinsin.kinsinutil

import android.util.Base64
import java.security.MessageDigest
import java.security.SecureRandom
import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec

/**
 * 加密工具类
 *
 * @author kinsin
 * <p>
 *
 * <h3>关于哈希算法</h3>
 * 为确保在使用随机盐的情况下正确验证原始字符串的相等性，需遵循以下步骤：
 *
 * <b>核心原则</b><br>
 * 盐的唯一性 + 盐的存储 = 安全验证<br>
 * 每次哈希必须使用 相同的盐 才能验证一致性。
 *
 * <h4>完整操作流程</h4>
 * <b>1. 注册/存储阶段</b><br>
 * 生成随机盐：每次为新数据（如用户密码）生成唯一盐值。<br>
 * 加盐哈希：使用 md5WithSalt(原始字符串, 盐) 计算哈希值。<br>
 * 存储盐和哈希值：将 盐 和 哈希结果 一同保存（如数据库的两个字段）。
 * <pre>{@code
 * // 示例：用户注册时保存密码
 * val password = "user123"
 * val salt = CryptoUtils.generateSalt()
 * val hashedPassword = CryptoUtils.md5WithSalt(password, salt)
 *
 * // 将 salt 和 hashedPassword 存入数据库
 * }</pre>
 *
 * <b>2. 验证阶段</b><br>
 * 取出盐值：从数据库获取该数据对应的盐。<br>
 * 重新计算哈希：对用户输入的字符串使用 相同盐值 再次哈希。<br>
 * 比较结果：检查新哈希值是否与存储的哈希值一致。
 * <pre>{@code
 * // 示例：用户登录时验证密码
 * val inputPassword = "user123"  // 用户输入的密码
 * val storedSalt = ...           // 从数据库读取该用户的盐
 * val storedHashedPassword = ... // 从数据库读取该用户的哈希值
 *
 * // 使用相同盐重新计算哈希
 * val newHash = CryptoUtils.md5WithSalt(inputPassword, storedSalt)
 *
 * // 比较是否一致
 * val isMatch = (newHash == storedHashedPassword)
 * }</pre>
 */
object CryptoUtils {

    // region Hash 算法（不可逆 含盐）
    /**
     * MD5 加盐哈希（更安全）
     * @param input 原始字符串
     * @param salt 盐值（建议使用 generateSalt() 生成）
     * @return 32位小写MD5字符串
     */
    fun md5WithSalt(input: String, salt: ByteArray): String {
        return hashWithSalt(input, salt, "MD5")
    }

    /**
     * SHA-256 加盐哈希（更安全）
     * @param input 原始字符串
     * @param salt 盐值（建议使用 generateSalt() 生成）
     * @return 64位小写SHA-256字符串
     */
    fun sha256WithSalt(input: String, salt: ByteArray): String {
        return hashWithSalt(input, salt, "SHA-256")
    }

    /** 通用加盐哈希方法 */
    private fun hashWithSalt(input: String, salt: ByteArray, algorithm: String): String {
        val digest = MessageDigest.getInstance(algorithm)
        digest.reset()
        digest.update(salt) // 先加盐
        val hashed = digest.digest(input.toByteArray(Charsets.UTF_8))
        return bytesToHex(hashed)
    }

    /** 生成随机盐（推荐长度16字节） */
    fun generateSalt(length: Int = 16): ByteArray {
        val salt = ByteArray(length)
        SecureRandom().nextBytes(salt) // 使用安全随机数生成盐
        return salt
    }

    /**
     * MD5 加密
     * @param input 原始字符串
     * @return 32位小写MD5字符串
     */
    fun md5(input: String): String = hashString(input, "MD5")

    /**
     * SHA-256 加密
     * @param input 原始字符串
     * @return 64位小写SHA-256字符串
     */
    fun sha256(input: String): String = hashString(input, "SHA-256")

    private fun hashString(input: String, algorithm: String): String {
        return MessageDigest.getInstance(algorithm)
            .digest(input.toByteArray())
            .joinToString("") { "%02x".format(it) }
    }
    // endregion

    // region AES 加解密 (可逆)
    /**
     * 生成随机AES密钥（256位）
     */
    fun generateAESKey(): ByteArray {
        val key = ByteArray(32) // 256位
        SecureRandom().nextBytes(key)
        return key
    }

    /**
     * AES加密
     * @param data 明文数据
     * @param key 密钥（支持128/192/256位）
     * @return Base64编码的加密结果（含IV）
     */
    fun encryptAES(data: String, key: ByteArray): String {
        val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
        val iv = ByteArray(16).apply { SecureRandom().nextBytes(this) }
        val ivSpec = IvParameterSpec(iv)
        cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(key, "AES"), ivSpec)
        
        val encrypted = cipher.doFinal(data.toByteArray(Charsets.UTF_8))
        val combined = iv + encrypted // 拼接IV和密文
        return Base64.encodeToString(combined, Base64.DEFAULT)
    }

    /**
     * AES解密
     * @param encryptedData Base64编码的加密结果（含IV）
     * @param key 使用的密钥
     */
    fun decryptAES(encryptedData: String, key: ByteArray): String {
        val combined = Base64.decode(encryptedData, Base64.DEFAULT)
        
        val iv = combined.copyOfRange(0, 16)
        val cipherBytes = combined.copyOfRange(16, combined.size)
        
        val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
        cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv))
        
        return String(cipher.doFinal(cipherBytes), Charsets.UTF_8)
    }
    // endregion

    // region Base64 工具
    fun encodeBase64(data: ByteArray): String = 
        Base64.encodeToString(data, Base64.NO_WRAP)

    fun decodeBase64(base64Str: String): ByteArray = 
        Base64.decode(base64Str, Base64.NO_WRAP)
    // endregion

    // 备用：将字节数组转十六进制字符串
    fun bytesToHex(bytes: ByteArray): String = 
        bytes.joinToString("") { "%02x".format(it) }
}